﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Caliburn.Micro;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.IO;
using System.Windows;
using GTServices.Entities;
using GTServices.Model.Extensions;
using System.Text.RegularExpressions;
using System.Threading;


namespace GTServices.Model.CoRoutine
{
    public class ExecuteDirectoryGrepRequestRoutine : IResult
    {
        public event EventHandler<ResultCompletionEventArgs> Completed;

        public delegate void NextDirectory(String dir, ExecuteDirectoryGrepRequestRoutine sender);
        public NextDirectory OnNextDirectory { get; set; } 


        public GrepRequest Request { get; protected set; }
        public GrepResult Result { get; protected set; }

        public ExecuteDirectoryGrepRequestRoutine(GrepRequest request)
        {
            this.Request = request;
        }

        public void Execute(ActionExecutionContext context)
        {
            GTApplication.Instance.DataModel.GrepRequests.Add(Request);

            try
            {

                //Timestamp the request. 
                Request.Start = DateTime.Now;

                //Create a results set. 
                BindableCollection<MatchInfo> matches = new BindableCollection<MatchInfo>();

                //Get all the langauges for the patterns. 
                var languages = Request.Patterns.Where(x=> x.Language != null).Select(x => x.Language).Distinct();
                IList<FileExtension> exts = null;

                if (Request.FileExtensions != null && Request.FileExtensions.Count > 0)
                {
                    exts = Request.FileExtensions;
                }
                else
                {
                    var extensions = languages.Where(y => !y.Name.Equals("All")).SelectMany(x => x.FileExtensions).Distinct().ToList();
                }
                
                var patterns = Request.Patterns.Distinct().ToList();

                var dirs = Directory.EnumerateDirectories(Request.RootPath, "*", SearchOption.AllDirectories).ToList();
                dirs.Add(Request.RootPath); 

                foreach(string dir in dirs)
                {
                    //Filtered?
                    var dirFilters = GTApplication.Instance.FilesModel.DirectoryFilters;

                    bool skip = false;
                    foreach (String pattern in dirFilters)
                    {
                        if (Regex.Match(dir, pattern).Success == true)
                        {
                            skip = true;
                            break;
                        }
                    }

                    if (skip)
                        continue;


                    //I should break this up, but for now whatever. 
                    Language l = null; 
                    if(this.Request.Languages != null)
                        l = this.Request.Languages.FirstOrDefault(x => x.Name == "All File Types"); 

                   


                    //Do we have the "all file type" language selected. If so all files are valid EXCEPT the ones listed.

                    var files = Directory.EnumerateFiles(dir, "*.*", SearchOption.TopDirectoryOnly).Select<String, Entities.FileInfo>(x =>
                    {
                        FileExtension extension = null;

                        if (l != null)
                        {
                            string extStr = Path.GetExtension(x);

                            extension = l.FileExtensions.FirstOrDefault(y => y.Extension.Equals(extStr, StringComparison.CurrentCultureIgnoreCase));

                            if (extension != null)
                            {
                                extension = null; // In otherwords, skip the file. 
                            }
                            else
                            {
                                extension = GTServices.Model.GTApplication.Instance.DataModel.DatabaseContext.FileExtensions.Create();
                                extension.Extension = extStr;
                            }
                        }
                        else
                        {
                            extension= exts.FirstOrDefault(y => y.Extension.Equals(Path.GetExtension(x), StringComparison.CurrentCultureIgnoreCase));
                        }

                        if (extension != null)
                        {
                            var fileInfo = new Entities.FileInfo();
                            string fileName = Path.GetFileName(x);
                            string path = Path.GetDirectoryName(x);

                            fileInfo.Name = fileName;
                            fileInfo.Path = Path.GetDirectoryName(x);
                            fileInfo.FileExtension = extension;

                            return fileInfo;
                        }
                        return null;
                    }).Where(y=> y != null).ToList();

                    if (files.Count < 1)
                    {
                        continue;
                    }

                    if (OnNextDirectory != null)
                    {
                        OnNextDirectory(dir, this);
                    }

                   
                    if (files != null)
                    {
                        if (files.Count > 0)
                        {
                            GrepRoutine gfr = new GrepRoutine(files, patterns, Request.LinesBefore, Request.LinesAfter);
                            gfr.Completed += (z, args) =>
                            {
                                gfr.Matches.ForEach(x => x.GrepRequest = Request); 

                                if (args.Error == null)
                                    matches.AddRange(gfr.Matches);
                                else
                                    throw args.Error;
                            };


                            //Execute Async. 
                            gfr.Execute(null);
                        }
                        //No files to grep.. let's do the next language, so do nothing here we will drop to the next iteration. 
                    }
                    else
                    {
                        throw new ArgumentException("Null fileInfos..this shouldn't happen");
                    }
                }

                //We are done! let's build the grep result. 
                Result = new GrepResult()
                {
                    GrepRequest = Request,
                    MatchInfos = matches,
                    TimeCompleted = DateTime.Now
                };


                if (Completed != null)
                {
                    Completed(this, new ResultCompletionEventArgs()); 
                }
            }
            catch(Exception e)
            {
                if (Completed != null)
                {
                    Completed(this, new ResultCompletionEventArgs() { Error = e });
                }
            }
        }
    }
}
